package com.example.rbacmanage.service.impl;

import com.example.rbacmanage.config.MinioConfig;
import com.example.rbacmanage.entity.FileUploadDetail;
import com.example.rbacmanage.exception.MyException;
import com.example.rbacmanage.mapper.FileUploadDetailMapper;
import com.example.rbacmanage.service.FileUploadService;
import com.github.pagehelper.PageInfo;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;

@Service
public class FileUploadServiceImpl implements FileUploadService {
    @Autowired
    private FileUploadDetailMapper fileUploadDetailMapper;
    @Autowired
    private MinioConfig minioConfig;
    @Autowired
    private MinioClient minioClient;
    @Override
    public void save(FileUploadDetail fileUploadDetail) {

    }

    @Override
    public void remove(Long[] ids) {

    }

    @Override
    public void update(FileUploadDetail fileUploadDetail) {

    }

    @Override
    public List<FileUploadDetail> getAll(FileUploadDetail fileUploadDetail) {
        return null;
    }

    @Override
    public PageInfo<FileUploadDetail> getPage(FileUploadDetail fileUploadDetail) {
        return null;
    }

    @Override
    public FileUploadDetail getById(Long id) {
        return null;
    }

    @Override
    public Integer getCount(FileUploadDetail fileUploadDetail) {
        return null;
    }

    @Override
    public void checkRepeat(FileUploadDetail fileUploadDetail) throws MyException {

    }

    @Override
    public void uploadChunk(MultipartFile chunk, int chunkNumber, String md5) {
        //文件块上传到minio
        String chunkName = md5+"-"+chunkNumber;
        try(InputStream inputStream = chunk.getInputStream()){
            minioClient.putObject(
                    PutObjectArgs.builder().bucket(minioConfig.getBucketName()).object(chunkName)
                            .stream(inputStream, chunk.getSize(), -1)
                            .contentType(chunk.getContentType())
                            .build());
        }catch (Exception e){
            e.printStackTrace();
        }
        //更新数据库
        FileUploadDetail fileUploadDetail= fileUploadDetailMapper.selectFileUploadDetailByMd5(md5);
        if (fileUploadDetail == null){
            fileUploadDetail = new FileUploadDetail();
            fileUploadDetail.setUsername(SecurityContextHolder.getContext().getAuthentication().getName());
            fileUploadDetail.setMd5(md5);
            fileUploadDetail.setHasBeenUploaded(String.valueOf(chunkNumber));
            fileUploadDetail.setCreateTime(new Date());
            fileUploadDetail.setTotalChunks(1);
            fileUploadDetailMapper.insertFileUploadDetail(fileUploadDetail);
        }else{
            Set<Integer> uploadChunks = new HashSet<>();
            if (fileUploadDetail.getHasBeenUploaded() != null){
                //将已上传的块编号从字符串转换成集合
                uploadChunks = Arrays.stream(fileUploadDetail.getHasBeenUploaded().split(":"))
                        .map(Integer::parseInt).collect(Collectors.toSet());
            }
            //添加当前已上传的块编号
            uploadChunks.add(chunkNumber);
            //更新已上传的块编号字符串
            String updatedChunks = uploadChunks.stream().map(String::valueOf).collect(Collectors.joining(":"));
            fileUploadDetail.setHasBeenUploaded(updatedChunks);
            fileUploadDetail.setUpdateTime(new Date());
            fileUploadDetail.setTotalChunks(uploadChunks.size());
            fileUploadDetailMapper.updateFileUploadDetail(fileUploadDetail);
        }
    }

    /**
     * 根据MD5获取已上传的文件块编号
     * @param md5
     * @return
     */
    @Override
    public Set<Integer> getUploadedChunks(String md5) {
        FileUploadDetail detail = fileUploadDetailMapper.selectFileUploadDetailByMd5(md5);
        if (detail == null || detail.getHasBeenUploaded() == null){
            return Collections.emptySet();
        }
        return Arrays.stream(detail.getHasBeenUploaded().split(":")).map(Integer::parseInt).collect(Collectors.toSet());
    }

    /**
     * 合并文件块
     * @param md5 文件的md5值
     * @param fileName 文件名
     * @return
     */
    @Override
    public String mergeChunks(String md5, String fileName) throws Exception {
        CopyOnWriteArraySet<ComposeSource> sourcesObjectList = new CopyOnWriteArraySet<>();
        //从数据库中获取文件上传详细信息
        FileUploadDetail fileUploadDetail = fileUploadDetailMapper.selectFileUploadDetailByMd5(md5);
        if (fileUploadDetail == null){
            throw new Exception("没有记录");
        }
        //获取所有已上传的块编号
        String[] chunkNames = fileUploadDetail.getHasBeenUploaded().split(":");
        for (String chunkName:chunkNames){
            try{
                //验证资源是否存在，释放流，避免资源占用
                try(InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minioConfig.getBucketName()).object(md5+"-"+chunkName).build())){
                    //添加到合并列表
                    sourcesObjectList.add(ComposeSource.builder().bucket(minioConfig.getBucketName()).object(md5+"-"+chunkName).build());
                }catch (Exception e){
                    throw new Exception("chunk not fund");
                }
            }catch (Exception e){
                //处理minio资源不存在的情况
                try{
                    removeChildFile(fileName,md5,1);
                    fileUploadDetailMapper.deleteFileUploadDetail(md5);
                }catch (Exception e1){

                }
                throw new Exception("操作失败");
            }
        }
        //合并文件块
        minioClient.composeObject(ComposeObjectArgs.builder().bucket(minioConfig.getBucketName()).object(fileName).sources(sourcesObjectList.stream().toList()).build());
        //生成文件的预签名URL
        String url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(minioConfig.getBucketName()).object(fileName).build());
        //更新数据库
        fileUploadDetail.setIsUploaded(1);
        fileUploadDetail.setUrl(url);
        fileUploadDetail.setFileName(fileName);
        fileUploadDetail.setUpdateTime(new Date());
        fileUploadDetailMapper.updateFileUploadDetail(fileUploadDetail);
        removeChildFile(fileName,md5,2);
        return url;
    }

    public void removeChildFile(String fileName,String md5,Integer type){
        //合并成功后，删除分开的文件
        try{
            List<DeleteObject> deleteObjects = new ArrayList<>();
            //列出所有匹配前缀的文件并添加到删除列表
            Iterable<Result<Item>> findMD5Files = minioClient.listObjects(ListObjectsArgs.builder().bucket(minioConfig.getBucketName()).prefix(md5).recursive(true).build());
            for (Result<Item> findMD5File : findMD5Files){
                Item item = findMD5File.get();
                deleteObjects.add(new DeleteObject(item.objectName()));
            }
            //type=1删除所有  type=2保留合并后的文件
            if (type == 1){
                deleteObjects.add(new DeleteObject(fileName));
            }
            //之下批量删除
            Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder()
                    .bucket(minioConfig.getBucketName())
                    .objects(deleteObjects).build());
            for (Result<DeleteError> result : results){
                DeleteError error=result.get();
            }
        }catch (Exception e1){

        }
    }
}
